/**
 * 可取消的Promise类型，扩展了cancel方法
 * @template R - Promise返回值类型
 */
export interface CancellablePromise<R> extends Promise<R> {
  /**
   * 取消Promise执行的方法
   */
  cancel?: () => void;
}

/**
 * useApi 主函数类型接口
 * @template Q - 基础参数类型
 * @template OQ - outputModel 方法处理后参数类型
 * @template R - 接口返回值类型
 * @template RD - inputModel 返回值处理后结果类型
 * @template L - loading 对象类型
 * @template LK - loading 对象键名类型
 * @template LS - loadingStart 方法返回值类型
 */
export interface useApiModel<
  Q extends object,
  OQ extends object | void,
  R extends object | unknown | void,
  RD extends object | void,
  L extends object | undefined,
  LK extends keyof L,
  LS
> {
  /**
   * 用于接口请求，规范代码结构
   * @param api - 接口方法
   * @param options - 配置选项
   * @returns [apiFunc, loading, apiObj]
   */
  (
    api: (arg?: OQ, opt?: LK) => CancellablePromise<R>,
    options?: useApiOptionsModel<R, RD, Q, OQ, L, LK, LS>
  ): [
      useApiFuncModel<R, RD, Q, LK>,
      useApiOptionsModel<R, RD, Q, OQ, L, LK, LS>['loading'],
      useApiObjStatusModel<R, RD, LK, Q, OQ>
    ]
}

/**
 * Api调用函数类型
 * @template R - 接口返回值类型
 * @template RD - inputModel 返回值处理后结果类型
 * @template Q - 基础参数类型
 * @template LK - loading 对象键名类型
 */
export interface useApiFuncModel<R, RD, Q, LK> {
  /**
   * 调用API函数
   * @param query - 接口请求参数
   * @param loadingKey - 指定响应对应的loading键名
   * @returns 调用结果Promise
   */
  (query?: { [T in keyof Q]?: Q[T] }, loadingKey?: LK): Promise<useApiResDataModel<R, RD, LK>>
}

/**
 * useApi 配置选项类型
 * @template R - 接口返回值类型
 * @template RD - inputModel 返回值处理后结果类型
 * @template Q - 基础参数类型
 * @template OQ - outputModel 方法处理后参数类型
 * @template L - loading 对象类型
 * @template LK - loading 对象键名类型
 * @template LS - loadingStart 方法返回值类型
 */
export interface useApiOptionsModel<R, RD, Q, OQ, L, LK, LS> {
  /** 重复调用，关闭历史接口调用，只采用最新结果 */
  readonly isCancel?: boolean

  /** 重复调用开关 */
  readonly isRepeat?: boolean

  /** 单例模式开关，取第一次调用结果 */
  readonly isSingle?: boolean

  /** loading 状态 */
  readonly loading?: L

  /** 预设接口参数 */
  readonly params?: () => Q

  /** 
   * 请求接口之前的函数
   * true & void 继续请求；false & Promise<reject> 阻止请求；
   */
  readonly before?: (params: Q) => boolean | void | Promise<any>

  /** 输出数据处理 */
  readonly outputModel?: (params: Q) => OQ

  /** 输入数据处理 */
  readonly inputModel?: (params: R) => RD

  /** loading开始回调 */
  readonly loadingStart?: (params: Q, loadingKey: LK) => LS

  /** loading结束回调 */
  readonly loadingEnd?: (params: LS) => void

  /** 请求接口成功回调 */
  readonly success?: (data: RD, loadingKey: LK, res: R) => void

  /** 请求接口失败回调 */
  readonly error?: (err: string | Error | undefined) => void
}

/**
 * Api对象状态类型
 * @template R - 接口返回值类型
 * @template RD - inputModel 返回值处理后结果类型
 * @template LK - loading 对象键名类型
 * @template Q - 基础参数类型
 * @template OQ - outputModel 方法处理后参数类型
 */
export interface useApiObjStatusModel<R, RD, LK extends string | number | symbol, Q, OQ> {
  /** Api请求的状态，0 待请求, 1 请求中, 2 成功, 3 失败 */
  status: 0 | 1 | 2 | 3

  /** 根据 loadingKey 来记录请求次数 */
  sendNum: { [loadingKey in LK]?: number }

  /** 最新请求的 loadingKey */
  sendLatestKey?: LK

  /** 最新一次请求结果 */
  pm?: CancellablePromise<R>

  /** 请求错误结果记录 */
  err?: string | Error

  /** 请求参数记录 */
  params?: Q

  /** 输出参数数据记录 */
  output?: OQ

  /** 结果数据 */
  result: useApiResDataModel<R, RD, LK>

  /** 关闭正在请求的Api */
  cancelApi: () => Promise<void>
}

/**
 * Api调用结果类型
 * @template R - 接口返回值类型
 * @template RD - inputModel 返回值处理后结果类型
 * @template LK - loading 对象键名类型
 */
export interface useApiResDataModel<R, RD, LK> {
  /** 未处理前的数据 */
  res: R

  /** 经过 inputModel 处理后的结果 */
  data: RD

  /** loading键名 */
  loadingKey: LK
}

/**
 * 数据类型辅助方法接口
 */
export interface UseDataAttrsModel {
  /**
   * 获取数据类型
   * @param data - 需要检测类型的数据
   * @returns 数据类型字符串 ('null' | 'undefined' | 'object' | 'array' | 'number' | 'string' | 'set')
   */
  getDataType(data: any): string

  /**
   * 合并数据，只有类型相同的数据才会被合并
   * @param data - 原始数据
   * @param cover - 需要合并的数据
   * @returns 合并后的数据
   */
  mergeData<T extends { [k: string]: any }, C extends { [k: string]: any }>(data: T, cover?: C): T
}

/**
 * useData 主函数类型接口
 */
export interface UseDataFunction {
  /**
   * 数据声明hook
   * @param func - 返回初始数据的函数
   * @returns [数据模型, 数据更新函数]
   */
  <R extends { [k: string]: any }>(func: () => R): [R, (coverData?: { [K in keyof R]?: R[K] }, isReset?: boolean) => R]
}

/**
 * useData 完整类型定义
 * 既是函数又具有附加属性方法的对象
 */
export type UseDataModel = UseDataFunction & UseDataAttrsModel